#ifndef _U_MATH_H_
#define _U_MATH_H_

#include "common/common.h"
#include "common/type.h"

namespace UCore
{
    using UVector4 = glm::vec4;
    using UVector3 = glm::vec3;
    using UVector2 = glm::vec2;

    using UMatrix3 = glm::mat3;
    using UMatrix4 = glm::mat4;

    using URotate3 = glm::vec3;
    using URotate2 = glm::vec2;

    using UQuaternion = glm::quat;

    struct UTransform3{
        UVector3 Position;
        URotate3 Rotate;
        UVector3 Scale;
    };

    struct UTransform2{
        UVector2 Position;
        URotate2 Rotate;
        UVector2 Scale;
    };

    // Constant

    const float32 Pi();
    const float32 HalfPi();
    const float32 TwoPi();
    const float32 Epsilon();

    constexpr UVector2 Vector2AxisX = {1.0f, 0};
    constexpr UVector2 Vector2AxisY = {0, 1.0f};
    constexpr UVector3 Vector3AxisX = {1.0f, 0, 0};
    constexpr UVector3 Vector3AxisY = {0, 1.0f, 0};
    constexpr UVector3 Vector3AxisZ = {0, 0, 1.0f};

    uBoolean NearBy(const float32 a, const float32 b, float32 deviation);
    uBoolean NearBy(const UVector2 a, const UVector2 b, float32 deviation);
    uBoolean NearBy(const UVector3 a, const UVector3 b, float32 deviation);
    uBoolean NearBy(const UVector4 a, const UVector4 b, float32 deviation);

    uBoolean Equal(const float32 a, float32 b);
    uBoolean Equal(const UVector2 a, const UVector2 b);
    uBoolean Equal(const UVector3 a, const UVector3 b);
    uBoolean Equal(const UVector4 a, const UVector4 b);

    //
    float32 Sin(float32 x);
    float32 Cos(float32 x);
    float32 Tan(float32 x);
    float32 Arcsin(float32 x);
    float32 Arccos(float32 x);
    float32 Atctan(float32 x);
    float32 Exp(float32 x);
    float32 Power(float32 x, float32 r);

    // use fast square root
    int32 Sqrt(int32 x);
    float32 Sqrt(float32 x);

    float32 Clamp(float32 x, float32 min, float32 max);
    //
    int32 Ceil(float32 x);
    int32 Floor(float32 x);

    float32 Min(float32 a,float32 b);
    float32 Max(float32 a,float32 b);

    //linear random number

    int32 Range(int32 min, int32 max);
    float32 Range(float32 min, float32 max);
    UVector2 Range(UVector2 min, UVector2 max);
    UVector3 Range(UVector3 min, UVector3 max);
    UVector4 Range(UVector4 min, UVector4 max);

    // Vector operate

    float32 Dot(const UVector3 &vec1, const UVector3 &vec2);
    UVector3 Corss(const UVector3 &vec1, const UVector3 &vec2);

    float32 Lerp(const float32 min, const float32 max, const float32 alpha);
    UVector2 Lerp(const UVector2 &vec1, const UVector2 &vec2, float32 alpha);
    UVector3 Lerp(const UVector3 &vec1, const UVector3 &vec2, float32 alpha);
    UVector4 Lerp(const UVector4 &vec1, const UVector4 &vec2, float32 alpha);
    UQuaternion Lerp(const UQuaternion &quat1, const UQuaternion &quat2, float32 alpha);

    UVector2 Normalize(const UVector2 &vec);
    UVector3 Normalize(const UVector3 &vec);
    UVector4 Normalize(const UVector4 &vec);
    const UVector2 &Normalize(UVector2 &vec);
    const UVector3 &Normalize(UVector3 &vec);
    const UVector4 &Normalize(UVector4 &vec);

    // Vector Translate

    UVector2 Translate(const UVector2 &vec, const UVector2 &trans);
    const UVector2 &Translate(UVector2 &vec, const UVector2 &trans);
    UVector3 Translate(const UVector3 &vec, const UVector3 &trans);
    const UVector3 &Translate(UVector3 &vec, const UVector3 &trans);
    UVector4 Translate(const UVector4 &vec, const UVector4 &trans);
    const UVector4 &Translate(UVector4 &vec, const UVector4 &trans);

    // Vector Rotate, glm/gtx/rotate_vec

    UVector2 RotateX(const UVector2 &vec, float32 angle);
    const UVector2 &RotateX(UVector2 &vec, float32 angle);
    UVector3 RotateX(const UVector3 &vec, float32 angle);
    const UVector3 &RotateX(UVector3 &vec, float32 angle);
    UVector4 RotateX(const UVector4 &vec, float32 angle);
    const UVector4 &RotateX(UVector4 &vec, float32 angle);

    UVector2 RotateY(const UVector2 &vec, float32 angle);
    const UVector2 &RotateY(UVector2 &vec, float32 angle);
    UVector3 RotateY(const UVector3 &vec, float32 angle);
    const UVector3 &RotateY(UVector3 &vec, float32 angle);
    UVector4 RotateY(const UVector4 &vec, float32 angle);
    const UVector4 &RotateY(UVector4 &vec, float32 angle);

    UVector2 RotateZ(const UVector2 &vec, float32 angle);
    const UVector2 &RotateZ(UVector2 &vec, float32 angle);
    UVector3 RotateZ(const UVector3 &vec, float32 angle);
    const UVector3 &RotateZ(UVector3 &vec, float32 angle);
    UVector4 RotateZ(const UVector4 &vec, float32 angle);
    const UVector4 &RotateZ(UVector4 &vec, float32 angle);

    // Vector Scale

    UVector2 Scale(const UVector2 &vec, const UVector2 &scale);
    const UVector2 &Scale(UVector2 &vec, const UVector2 &scale);
    UVector3 Scale(const UVector3 &vec, const UVector3 &scale);
    const UVector3 &Scale(UVector3 &vec, const UVector3 &scale);
    UVector4 Scale(const UVector4 &vec, const UVector4 &scale);
    const UVector4 &Scale(UVector4 &vec, const UVector4 &scale);

    // Matrix Translate

    UMatrix3 Translate(const UMatrix3 &mat, const UVector3 &trans);
    const UMatrix3 &Translate(UMatrix3 &mat, const UVector3 &trans);
    UMatrix4 Translate(const UMatrix4 &mat, const UVector3 &trans);
    const UMatrix4 &Translate(UMatrix4 &mat, const UVector3 &trans);

    // Matrix Rotate Euler

    UMatrix4 Rotate(const UMatrix4 &mat, const URotate3 &axis, float32 angle);
    const UMatrix4 &Rotate(UMatrix4 &mat, const URotate3 &axis, float32 angle);

    // Matrix Rotate Quaternion

    UMatrix4 Rotate(const UMatrix4 &mat, const UQuaternion &quat);
    const UMatrix4 &Rotate(UMatrix4 &mat, const UQuaternion &quat);

    // Matrix Scale

    UMatrix4 Scale(const UMatrix4 &mat, const UVector3 &scale);
    const UMatrix4 &Scale(UMatrix4 &mat, const UVector3 &scale);

    // Matrix Transpose
    UMatrix3 Transpose(const UMatrix3 &mat);
    const UMatrix3 &Transpose(UMatrix3 &mat);
    UMatrix4 Transpose(const UMatrix4 &mat);
    const UMatrix4 &Transpose(UMatrix4 &mat);

}

#endif